Skip to content

fix: feed image thumbnails + mobile layout regressions#1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher NiallJoeMaher requested a review from a team as a code owner June 14, 2026 07:56
@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
codu Ready Ready Preview, Comment Jun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s) Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s) Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/feed-images-and-mobile-layout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

NiallJoeMaher and others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.

- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
  callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens

The relaunch redesign had several mobile regressions:

- cards overflowed the viewport (byline meta line couldn't shrink) — now
  truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
  drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
  gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
  free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
  under them, and no longer sit in an overflow container that clipped the
  FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
  (blue-resolving) focus ring

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher NiallJoeMaher force-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679d Compare June 14, 2026 08:04
@NiallJoeMaher NiallJoeMaher merged commit dd621f2 into develop Jun 14, 2026
3 of 5 checks passed
@NiallJoeMaher NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant